﻿using PortUsedKill.Model.CustomException;
using PortUsedKill.Model.Entity;
using PortUsedKill.util;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using static PortUsedKill.Model.Entity.ProcessInfo;

namespace PortUsedKill
{
    public partial class PortMainFrm : UIForm
    {
        public PortMainFrm()
        {
            InitializeComponent();
        }

        private const int MAX_PATH = 260;
        public const int PROCESS_ALL_ACCESS = 0x000F0000 | 0x00100000 | 0xFFF;

        [DllImport("Kernel32.dll")]
        public extern static IntPtr OpenProcess(int fdwAccess, int fInherit, int IDProcess);

        [DllImport("psapi.dll")]
        static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In][MarshalAs(UnmanagedType.U4)] int nSize);
        [DllImport("Kernel32.dll")]
        public extern static bool CloseHandle(IntPtr hObject);

        [DllImport("shell32.dll")]
        private static extern int ExtractIconEx(string lpszFile, int niconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, int nIcons);


        /// <summary>
        /// 扫描端口占用信息列表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnScan_Click(object sender, EventArgs e)
        {
            uiBtnScan.Enabled = false;
            uiTxtPort.Enabled = false;
            uiLalPrompt.Text = "正在扫描端口";
            uiProcessBar.Visible = true;
            uiProcessBar.Value = 0;
            uiDgvProcessList.Rows.Clear();
            new Thread(() => GetPortUsedInfo()).Start();
        }

        /// <summary>
        /// 端口搜索
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UiBtnSearch_Click(object sender, EventArgs e)
        {
            PortSearch();
        }

        private void UiTxtPort_ButtonClick(object sender, EventArgs e)
        {
            PortSearch();
        }

        /// <summary>
        /// 端口搜索框回车
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UiTxtPort_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.Enter)
            {
                return;
            }
            PortSearch();

        }

        private DateTime lastButtonClickTime = DateTime.MinValue;

        /// <summary>
        /// 端口搜索
        /// </summary>
        private void PortSearch()
        {
            string port = uiTxtPort.Text.Trim();
            if (port == null || "".Equals(port))
            {
                ShowWarningTip("请输入端口号！");
                return;
            }
            if (!port.IsInt())
            {
                ShowWarningTip("请输入正确的端口号");
                return;
            }

            // 计算当前时间与上一次按钮按下时间的间隔
            TimeSpan timeSinceLastButtonClick = DateTime.Now - lastButtonClickTime;
            // 如果间隔小于 1 秒
            if (timeSinceLastButtonClick < TimeSpan.FromSeconds(1))
            {
                ShowWarningTip("操作频率过快，请稍后再试");
                return;
            }
            // 更新上一次按钮按下时间为当前时间
            lastButtonClickTime = DateTime.Now;

            uiBtnScan.Enabled = false;
            uiTxtPort.Enabled = false;
            uiLalPrompt.Text = "正在搜索端口";
            uiProcessBar.Visible = true;
            uiProcessBar.Value = 0;
            uiDgvProcessList.Rows.Clear();
            new Thread(() => GetPortUsedInfo(port.ToInt())).Start();
        }

        private void GetPortUsedInfo()
        {
            GetPortUsedInfo(-1);
        }
        private void GetPortUsedInfo(int port)
        {
            List<ProcessInfo> processInfos = new List<ProcessInfo>();
            Process[] ps = Process.GetProcesses();
            List<Process> processes = new List<Process>(ps);
            List<string> rows = new List<string>();
            for (int j = 0; j <= 50; j++)
            {
                TcpConnectionTableHelper.MIB_TCPROW_OWNER_PID[] tcpProgressInfoTable = TcpConnectionTableHelper.GetAllTcpConnections();
                foreach (TcpConnectionTableHelper.MIB_TCPROW_OWNER_PID item in tcpProgressInfoTable)
                {
                    if (port != -1 && item.LocalPort != port && item.RemotePort != port)
                    {
                        continue;
                    }
                    string localAddress = string.Format("{0}:{1}", TcpConnectionTableHelper.GetIpAddress(item.localAddr), item.LocalPort);
                    string remoteAddress = string.Format("{0}:{1}", TcpConnectionTableHelper.GetIpAddress(item.remoteAddr), item.RemotePort);
                    ProcessInfoBulider bulider = Bulider();
                    bulider.LocalAddress(localAddress).RemoteAddress(remoteAddress).Port(item.LocalPort.ToString())
                        .RemoteIp(TcpConnectionTableHelper.GetIpAddress(item.remoteAddr));
                    string outputRow = string.Format("{0, -7}{1, -23}{2, -23}{3, -16}{4}", "TCP",
                       localAddress, remoteAddress, (TCP_CONNECTION_STATE)item.state, item.owningPid);
                    Process proname = processes.Find(x => x.Id == item.owningPid);
                    if (!rows.Contains(outputRow))
                    {
                        rows.Add(outputRow);
                        bulider.Type("TCP").State((TCP_CONNECTION_STATE)item.state)
                            .ProcessIdentification(item.owningPid);
                        if (proname != null)
                        {
                            bulider.ProcessName(proname.ProcessName);
                        }
                        ProcessInfo processInfo = bulider.Bulid();
                        // 比较慢，一般不会太多
                        processInfo.GetRemoteIpLocation();
                        processInfos.Add(processInfo);
                    }
                }
                this.Invoke(new Action(() =>
                {
                    uiProcessBar.Value += 2;
                }));
            }
            this.Invoke(
                new Action(() =>
                {
                    foreach (var item in processInfos)
                    {
                        int index = uiDgvProcessList.Rows.Add();
                        uiDgvProcessList.Rows[index].Cells["ProcessName"].Value = item.ProcessName;
                        uiDgvProcessList.Rows[index].Cells["Type"].Value = item.Type;
                        uiDgvProcessList.Rows[index].Cells["ProcessIdentification"].Value = item.ProcessIdentification;
                        uiDgvProcessList.Rows[index].Cells["Port"].Value = item.Port;
                        uiDgvProcessList.Rows[index].Cells["LocalAddress"].Value = item.LocalAddress;
                        uiDgvProcessList.Rows[index].Cells["RemoteAddress"].Value = item.RemoteAddress;
                        uiDgvProcessList.Rows[index].Cells["State"].Value = item.State;
                        uiDgvProcessList.Rows[index].Cells["RemoteIpLocation"].Value = item.RemoteIpLocation;
                    }
                    uiBtnScan.Enabled = true;
                    uiTxtPort.Enabled = true;
                    uiLalPrompt.Text = "扫描完成";
                    ShowSuccessTip("扫描完成");
                    System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer
                    {
                        Interval = 1500
                    };
                    timer.Tick += (s, e) => { uiProcessBar.Value = 0; uiProcessBar.Visible = false; timer.Stop(); };
                    timer.Start();
                    //new Thread(() =>
                    //{
                    //    GetRemoteIpLocation(processInfos);
                    //}).Start();
                })
                );
        }
        /// <summary>
        /// 获取远程IP地址
        /// </summary>
        private void GetRemoteIpLocation(List<ProcessInfo> processInfos)
        {
            foreach (ProcessInfo item in processInfos)
            {
                item.GetRemoteIpLocation();
            }
        }

        private void DgvProcessList_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (uiDgvProcessList.Columns[e.ColumnIndex].Name.Equals("ProcessIcon") && uiDgvProcessList.Rows[e.RowIndex].Cells["ProcessIcon"].Value == null)
            {
                int pid = (int)uiDgvProcessList.Rows[e.RowIndex].Cells["ProcessIdentification"].Value;
                Icon ico = null;
                IntPtr pHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
                StringBuilder sb = new StringBuilder(MAX_PATH);
                GetModuleFileNameEx(pHandle, IntPtr.Zero, sb, MAX_PATH);
                CloseHandle(pHandle);
                //获取图标
                IntPtr[] largeIcons, smallIcons;
                int IconCount = ExtractIconEx(sb.ToString(), -1, null, null, 0);
                largeIcons = new IntPtr[IconCount];
                smallIcons = new IntPtr[IconCount];
                ExtractIconEx(sb.ToString(), 0, largeIcons, smallIcons, IconCount);
                try
                {
                    IntPtr icon = largeIcons[0];
                    ico = Icon.FromHandle(icon);
                }
                catch (Exception ex)
                {
                    Console.Out.WriteLine(ex.Message);
                }
                if (ico != null)
                {
                    uiDgvProcessList.Rows[e.RowIndex].Cells["ProcessIcon"].Value = ico;
                }
                else
                {
                    uiDgvProcessList.Rows[e.RowIndex].Cells["ProcessIcon"].Value = Properties.Resources.win;
                }
            }
        }
        /// <summary>
        /// 右键菜单---终止此进程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToolStripMenuKill_Click(object sender, EventArgs e)
        {
            var selectRows = uiDgvProcessList.SelectedRows;
            if (selectRows.Count == 0)
            {
                return;
            }
            int pid = GetProcessIdentification();
            if (!ShowAskDialog($"确定终止PID为{pid}的进程吗？"))
            {
                return;
            }
            try
            {
                Process process = Process.GetProcessById(pid);
                if (process == null)
                {
                    ShowErrorTip("获取进程相关信息失败,请尝试重新操作");
                    return;
                }
                process.Kill();
                process.WaitForExit();
                process.Close();
                uiDgvProcessList.Rows.Remove(selectRows[0]);
                ShowSuccessTip("操作成功");
            }
            catch (Exception ex)
            {
                ShowErrorDialog("结束进程失败", ex.Message);
            }
        }
        /// <summary>
        /// 右键菜单---刷新进程列表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToolStripMenuRefresh_Click(object sender, EventArgs e)
        {
            BtnScan_Click(null, null);
        }
        /// <summary>
        /// 右键菜单---打开路径
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToolStripMenuOpenFilePath_Click(object sender, EventArgs e)
        {
            string filePath = GetProcessFilePath();
            if (filePath == null)
            {
                return;
            }
            try
            {
                if (!File.Exists(filePath) && !Directory.Exists(filePath))
                    return;
                if (Directory.Exists(filePath))
                    Process.Start(@"explorer.exe", "/select,\"" + filePath + "\"");
                else
                {
                    IntPtr pidlList = TcpConnectionTableHelper.ILCreateFromPathW(filePath);
                    if (pidlList != IntPtr.Zero)
                    {
                        try
                        {
                            Marshal.ThrowExceptionForHR(TcpConnectionTableHelper.SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0));
                        }
                        finally
                        {
                            TcpConnectionTableHelper.ILFree(pidlList);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ShowErrorDialog("系统错误", "打开文件夹失败\n" + ex.ToString());
            }
        }
        /// <summary>
        /// 右键菜单---复制完整路径
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToolStripMenuCopyFilePath_Click(object sender, EventArgs e)
        {
            string filePath = GetProcessFilePath();
            if (filePath == null)
            {
                return;
            }
            Clipboard.SetDataObject(filePath, true);
        }
        /// <summary>
        /// 右键菜单---属性
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToolStripMenuFileAttributes_Click(object sender, EventArgs e)
        {
            string filePath = GetProcessFilePath();
            if (filePath == null)
            {
                return;
            }
            TcpConnectionTableHelper.ShowFileProperties(filePath);
        }
        /// <summary>
        /// 获取该进程文件完整路径
        /// </summary>
        /// <returns></returns>
        private string GetProcessFilePath()
        {
            int pid = GetProcessIdentification();
            Process process = Process.GetProcessById(pid);
            if (process == null)
            {
                throw new ErrorTipException("获取进程相关信息失败,请尝试重新操作");
            }
            string filePath = process.MainModule.FileName;
            return filePath;
        }
        /// <summary>
        /// 获取进程PID
        /// </summary>
        /// <returns></returns>
        private int GetProcessIdentification()
        {
            var selectRows = uiDgvProcessList.SelectedRows;
            if (selectRows.Count == 0)
            {
                throw new ErrorTipException("没有选中任何行！");
            }
            return (int)selectRows[0].Cells["ProcessIdentification"].Value;
        }
        /// <summary>
        /// 执行CMD命令
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnExecute_Click(object sender, EventArgs e)
        {
            RichTextCmdView.Text = RunCmd(this.TxtCmd.Text);
            uiLalPrompt.Text = "执行成功";
        }
        /// <summary>
        /// 执行CMD命令
        /// </summary>
        /// <param name="command">cmd命令</param>
        /// <returns></returns>
        private string RunCmd(string command)
        {
            Process p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            //是否使用操作系统shell启动
            p.StartInfo.UseShellExecute = false;
            //接受来自调用程序的输入信息
            p.StartInfo.RedirectStandardInput = true;
            //由调用程序获取输出信息
            p.StartInfo.RedirectStandardOutput = true;
            //重定向标准错误输出
            p.StartInfo.RedirectStandardError = true;
            //不显示程序窗口
            p.StartInfo.CreateNoWindow = true;
            //启动程序
            p.Start();
            //向cmd窗口发送输入信息
            p.StandardInput.WriteLine(command + "&exit");
            p.StandardInput.AutoFlush = true;
            //p.StandardInput.WriteLine("exit");
            //向标准输入写入要执行的命令。这里使用&是批处理命令的符号，表示前面一个命令不管是否执行成功都执行后面(exit)命令，如果不执行exit命令，后面调用ReadToEnd()方法会假死
            //同类的符号还有&&和||前者表示必须前一个命令执行成功才会执行后面的命令，后者表示必须前一个命令执行失败才会执行后面的命令
            //获取cmd窗口的输出信息
            string output = p.StandardOutput.ReadToEnd();
            //等待程序执行完退出进程
            p.WaitForExit();
            p.Close();
            return output;
        }

        private void CboCmd_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (this.CboCmd.SelectedIndex)
            {
                case 0:
                    TxtCmd.Text = "netstat -ano";
                    break;
                case 1:
                    TxtCmd.Text = "netstat -ano|find \"\"";
                    break;
                case 2:
                    TxtCmd.Text = "tasklist |find \"\"";
                    break;
                case 3:
                    TxtCmd.Text = "taskkill /pid \"\" /f";
                    break;
                default:
                    break;
            }
        }

        private void TsmIssues_Click(object sender, EventArgs e)
        {
            Process.Start("https://gitee.com/lxm5201314/port-used-kill");
        }
    }
}
